/*
moxl - Modular XML Library
Copyright (c) 2007, Malcolm Sparks

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package moxl.compare;

import java.util.Collection;
import java.util.Iterator;

import moxl.dom.MoxlAttribute;
import moxl.dom.MoxlCharacterData;
import moxl.dom.MoxlElement;

public class DocumentComparator
{
	public DocumentDifferences compare(MoxlElement  first, MoxlElement second)
	{
		DocumentDifferences diffs = new DocumentDifferences();
		compare(first, second, diffs);
		return diffs;
	}

	private void compare(MoxlElement first, MoxlElement second, DocumentDifferences diffs)
	{
		if (first == null || second == null || diffs == null)
		{
			throw new IllegalArgumentException("Null argument passed");
		}
		if (!first.getName().equals(second.getName()))
		{
			diffs.add(new ElementNameDifference(first.getName(), second.getName()));
			return;
		}

		for (MoxlAttribute attribute : first.getAttributeMap())
		{
			MoxlAttribute otherAttribute = second.getAttributeObj(attribute.getQName());
			if (otherAttribute == null)
			{
				diffs.add(new AttributeDifference("Element " + first + " contains an attribute '" + attribute.getQName().getLocalPart()
				        + "' that does not exist in element " + second));
			}
			else
			{
				if (!attribute.getValue().equals(otherAttribute.getValue()))
				{
					diffs.add(new AttributeValueDifference("Attribute " + attribute + " has different values in element " + first + " and element " + second));
				}
			}
		}
		for (MoxlAttribute attribute : second.getAttributeMap())
		{
			MoxlAttribute otherAttribute = first.getAttributeObj(attribute.getQName());
			if (otherAttribute == null)
			{
				diffs.add(new AttributeDifference("Element " + second + " contains an attribute '" + attribute.getQName().getLocalPart()
				        + "' that does not exist in element " + first));
			}
		}

		if (first.getChildElements().isEmpty() && second.getChildElements().isEmpty())
		{
			if (first.getChildren().isEmpty() && second.getChildren().isEmpty())
			{
				return;
			}
			if (first.getChildren().size() == 1 && second.getChildren().size() == 1)
			{
				if (first.getChildNode(0) instanceof MoxlCharacterData && second.getChildNode(0) instanceof MoxlCharacterData)
				{
					compare((MoxlCharacterData) first.getChildNode(0), (MoxlCharacterData) second.getChildNode(0), diffs);
					return;
				}
				else
				{
					throw new UnsupportedOperationException("Don't know how to compare");
				}
			}
			else
			{
				diffs.add(new ExplainedDocumentDifference("Element " + first + " and element " + second + " differ in content"));
			}
			return;
		}
		else if (first.getChildElements().size() == second.getChildElements().size())
        {
			Collection<? extends MoxlElement> firstChildren = first.getChildElements();
			Collection<? extends MoxlElement> secondChildren = second.getChildElements();
			Iterator<? extends MoxlElement> firstIterator = firstChildren.iterator();
			Iterator<? extends MoxlElement> secondIterator = secondChildren.iterator();
			while (firstIterator.hasNext())
			{
				compare(firstIterator.next(), secondIterator.next(), diffs);
			}
		}
        else
        {
			diffs.add(new ElementChildElementsCountDifference(first, second));
			return;
		}
	}

	private void compare(MoxlCharacterData first, MoxlCharacterData second, DocumentDifferences diffs)
	{
		if (!first.getData().equals(second.getData()))
		{
			diffs.add(new CharacterDataDifference());
		}
	}
}
